﻿using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace KinonekoSoftware.Extensions.Collections
{
    public static partial class CollectionStatic
    {
        public static readonly PropertyChangedEventArgs         CountPropertyChanged   = new PropertyChangedEventArgs("Count");
        public static readonly PropertyChangedEventArgs         IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");
        public static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        private static void Empty<T>(T a, int b, int c){}



        //-------------------------------------------------------------
        //
        //          SelectProperty
        //
        //-------------------------------------------------------------


        #region SelectProperty
        
        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }

            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
            }

            return list;
        }

        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }


            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
            }

            return list;
        }

        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <param name="selector4">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3,
            Func<TClass, TProp> selector4)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }

            if (selector4 is null)
            {
                throw new ArgumentNullException(nameof(selector4));
            }


            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
                list.Add(selector4(item));
            }

            return list;
        }

        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <param name="selector4">属性选择器</param>
        /// <param name="selector5">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3,
            Func<TClass, TProp> selector4,
            Func<TClass, TProp> selector5)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }

            if (selector4 is null)
            {
                throw new ArgumentNullException(nameof(selector4));
            }

            if (selector5 is null)
            {
                throw new ArgumentNullException(nameof(selector5));
            }

            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
                list.Add(selector4(item));
                list.Add(selector5(item));
            }

            return list;
        }

        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <param name="selector4">属性选择器</param>
        /// <param name="selector5">属性选择器</param>
        /// <param name="selector6">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3,
            Func<TClass, TProp> selector4,
            Func<TClass, TProp> selector5,
            Func<TClass, TProp> selector6)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }

            if (selector4 is null)
            {
                throw new ArgumentNullException(nameof(selector4));
            }

            if (selector5 is null)
            {
                throw new ArgumentNullException(nameof(selector5));
            }

            if (selector6 is null)
            {
                throw new ArgumentNullException(nameof(selector6));
            }

            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
                list.Add(selector4(item));
                list.Add(selector5(item));
                list.Add(selector6(item));
            }

            return list;
        }

        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <param name="selector4">属性选择器</param>
        /// <param name="selector5">属性选择器</param>
        /// <param name="selector6">属性选择器</param>
        /// <param name="selector7">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3,
            Func<TClass, TProp> selector4,
            Func<TClass, TProp> selector5,
            Func<TClass, TProp> selector6,
            Func<TClass, TProp> selector7)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }

            if (selector4 is null)
            {
                throw new ArgumentNullException(nameof(selector4));
            }

            if (selector5 is null)
            {
                throw new ArgumentNullException(nameof(selector5));
            }

            if (selector6 is null)
            {
                throw new ArgumentNullException(nameof(selector6));
            }

            if (selector7 is null)
            {
                throw new ArgumentNullException(nameof(selector7));
            }

            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
                list.Add(selector4(item));
                list.Add(selector5(item));
                list.Add(selector6(item));
                list.Add(selector7(item));
            }

            return list;
        }


        
        /// <summary>
        /// 将一个类型的多个属性合并为一个数据流
        /// </summary>
        /// <param name="collection">原始数据流</param>
        /// <param name="selector1">属性选择器</param>
        /// <param name="selector2">属性选择器</param>
        /// <param name="selector3">属性选择器</param>
        /// <param name="selector4">属性选择器</param>
        /// <param name="selector5">属性选择器</param>
        /// <param name="selector6">属性选择器</param>
        /// <param name="selector7">属性选择器</param>
        /// <param name="selector8">属性选择器</param>
        /// <typeparam name="TClass">数据类型</typeparam>
        /// <typeparam name="TProp">属性类型</typeparam>
        /// <returns>返回一个新的数据流</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TProp> SelectProperty<TClass, TProp>(
            this ICollection<TClass> collection,
            Func<TClass, TProp> selector1,
            Func<TClass, TProp> selector2,
            Func<TClass, TProp> selector3,
            Func<TClass, TProp> selector4,
            Func<TClass, TProp> selector5,
            Func<TClass, TProp> selector6,
            Func<TClass, TProp> selector7,
            Func<TClass, TProp> selector8)
        {
            var list = new List<TProp>(32);

            if (collection is null)
            {
                return null;
            }

            if (selector1 is null)
            {
                throw new ArgumentNullException(nameof(selector1));
            }

            if (selector2 is null)
            {
                throw new ArgumentNullException(nameof(selector2));
            }


            if (selector3 is null)
            {
                throw new ArgumentNullException(nameof(selector3));
            }

            if (selector4 is null)
            {
                throw new ArgumentNullException(nameof(selector4));
            }

            if (selector5 is null)
            {
                throw new ArgumentNullException(nameof(selector5));
            }

            if (selector6 is null)
            {
                throw new ArgumentNullException(nameof(selector6));
            }

            if (selector7 is null)
            {
                throw new ArgumentNullException(nameof(selector7));
            }


            if (selector8 is null)
            {
                throw new ArgumentNullException(nameof(selector8));
            }

            foreach (var item in collection)
            {
                if (ReferenceEquals(item, default(TClass)))
                {
                    continue;
                }

                list.Add(selector1(item));
                list.Add(selector2(item));
                list.Add(selector3(item));
                list.Add(selector4(item));
                list.Add(selector5(item));
                list.Add(selector6(item));
                list.Add(selector7(item));
                list.Add(selector8(item));
            }

            return list;
        }

        #endregion
        
        
        //-------------------------------------------------------------
        //
        //          Diff
        //
        //-------------------------------------------------------------
        
        
        #region Diff


        public static void Diff<TItem, TKey>(
            IEnumerable<TItem> source,
            IEnumerable<TItem> target,
            Func<TItem, TKey> keySelector,
            out List<TItem> added,
            out List<TItem> modified,
            out List<TItem> removed)
        {
            // ReSharper disable PossibleMultipleEnumeration
            var sourceHash = new HashSet<TKey>(source.Select(keySelector));
            modified = new List<TItem>();
            added    = new List<TItem>();

            foreach (var item in target)
            {
                var key = keySelector(item);

                if (sourceHash.Remove(key))
                {
                    //
                    // 相同
                    modified.Add(item);
                }
                else
                {
                    added.Add(item);
                }
            }

            removed = source.Where(x => sourceHash.Contains(keySelector(x)))
                            .ToList();
        }

        #endregion
        
        
        
        //-------------------------------------------------------------
        //
        //          IndexOf
        //
        //-------------------------------------------------------------


        #region IndexOf

        

        

        #endregion
        
        
        //-------------------------------------------------------------
        //
        //          ForEach
        //
        //-------------------------------------------------------------


        #region ForEach

        
        public static void ForEach<T>(this IEnumerable<T> collection, Action<T> handler)
        {
            if (collection is null)
            {
                return;
            }

            if (handler is null)
            {
                return;
            }

            foreach (var item in collection)
            {
                handler(item);
            }
        }

        #endregion
        
        
        
        //-------------------------------------------------------------
        //
        //          ShiftUp
        //
        //-------------------------------------------------------------



        #region ShiftUp

                
        public static void ShiftUp<T>(this ObservableCollection<T> source, T target)
        {
            ShiftUp(source, target, Empty); 
        }
        
        
        public static void ShiftUp<T>(this ObservableList<T> source, T target)
        {
            ShiftUp(source, target, Empty); 
        }
        
        public static void ShiftUp<T>(this ObservableCollection<T> source, T target, Action callback)
        {
            ShiftUp(source, target, (_, _, _) => callback?.Invoke()); 
        }
        
        
        public static void ShiftUp<T>(this ObservableList<T> source, T target, Action callback)
        {
            ShiftUp(source, target, (_, _, _) => callback?.Invoke()); 
        }
        
        public static void ShiftUp<T>(this IList<T> source, T target)
        {
            ShiftUp(source, target, Empty); 
        }
        
        public static void ShiftUp<T>(this IList<T> source, T target, Action callback)
        {
            ShiftUp(source, target, (_, _, _) => callback?.Invoke()); 
        }

        public static void ShiftUp<T>(this IList<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);


            if (index < 1)
            {
                return;
            }

            (source[index], source[index - 1]) = (source[index - 1], source[index]);
            
            callback?.Invoke(target, index, index - 1);
        }

        public static void ShiftUp<T>(this ObservableCollection<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);

            if (index < 1)
            {
                return;
            }
            
            source.Move(index, index - 1);
            
            callback?.Invoke(target, index, index - 1);
        }
        
        public static void ShiftUp<T>(this ObservableList<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);

            if (index < 1)
            {
                return;
            }
            
            source.Move(index, index - 1);
            
            callback?.Invoke(target, index, index - 1);
        }
        
        #endregion

        
        
        //-------------------------------------------------------------
        //
        //          ShiftDown
        //
        //-------------------------------------------------------------



        #region ShiftDown

        public static void ShiftDown<T>(this ObservableCollection<T> source, T target)
        {
            ShiftDown(source, target, Empty); 
        }
        
        public static void ShiftDown<T>(this ObservableList<T> source, T target)
        {
            ShiftDown(source, target, Empty); 
        }
        
        public static void ShiftDown<T>(this IList<T> source, T target)
        {
            ShiftDown(source, target, Empty); 
        }
        
        public static void ShiftDown<T>(this IList<T> source, T target, Action callback)
        {
            ShiftDown(source, target, (_, _, _) => callback?.Invoke());
        }
        
        public static void ShiftDown<T>(this ObservableCollection<T> source, T target, Action callback)
        {
            ShiftDown(source, target, (_, _, _) => callback?.Invoke());
        }
        
        
        public static void ShiftDown<T>(this ObservableList<T> source, T target, Action callback)
        {
            ShiftDown(source, target, (_, _, _) => callback?.Invoke());
        }

        public static void ShiftDown<T>(this IList<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);

            if (index >= source.Count - 1)
            {
                return;
            }
            (source[index], source[index + 1]) = (source[index + 1], source[index]);
            callback?.Invoke(target, index, index + 1);
        }
        
        
        
        public static void ShiftDown<T>(this ObservableCollection<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);

            if (index >= source.Count - 1)
            {
                return;
            }
            
            source.Move(index, index + 1);
            callback?.Invoke(target, index, index + 1);
        }
        
        
        public static void ShiftDown<T>(this ObservableList<T> source, T target, Action<T, int, int> callback)
        {
            if (ReferenceEquals(target, default(T)))
            {
                return;
            }

            if (source is null)
            {
                return;
            }

            var index = source.IndexOf(target);

            if (index >= source.Count - 1)
            {
                return;
            }
            
            source.Move(index, index              + 1);
            callback?.Invoke(target, index, index + 1);
        }
        #endregion

    }
}